package com.project.dao;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.project.common.Page;
import com.project.entity.BaseEntity;
import com.project.util.ListUtil;
import com.project.util.PropertyUtils;

@Repository
public class BaseDao<T extends BaseEntity>{

	@Autowired
	protected SessionFactory sessionFactory;

	protected SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	protected void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory; 
	}

	protected Session getCurrentSession() {
		return this.sessionFactory.getCurrentSession();
	}

	public Serializable save(T o) {
	    setDr(o);
		return this.getCurrentSession().save(o);
	}
	
	public void update(T o) {
		this.getCurrentSession().update(o);
	}
	
	public void saveOrUpdate(T o) {
	    setDr(o);
		this.getCurrentSession().saveOrUpdate(o);
	}
	
	public void delete(T o) {
		this.getCurrentSession().delete(o);
	}
	
	public void delete(Long id) {
		this.delete(this.getById(id));
	}
	
	//关联表修改时使用
	public void clear() {
		this.getCurrentSession().clear();
	}

	/**
	 * 根据HQL查询实体
	 * @param hql
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected T get(String hql) {
		Query q = this.getCurrentSession().createQuery(hql);
		List<T> l = q.list();
		if (l != null && l.size() > 0) {
			return l.get(0);
		}
		return null;
	}
	
	/**
	 * 根据ID查询实体(该方法暂时停用)
	 * @param clazz
	 * @param id
	 * @return
	 */
	public T getById(Class<?> clazz, Serializable id) {
		T entity = (T)this.getCurrentSession().get(clazz, id);
		return entity;
	}
	
	/**
	 * 根据ID查询实体
	 * @param id
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public T getById(Long id){
		Type genType = getClass().getGenericSuperclass(); 
		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();   
		Class<T> entityClass =  (Class)params[0];  
		
		Object entity = this.getCurrentSession().get(entityClass, id);
		return (T) entity;
	}
	
	/**
	 * 根据HQL查询实体，带参数(该方法暂时停用)
	 * @param hql
	 * @param params
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private T get(String hql, Map<String, Object> params) {
		Query q = this.getCurrentSession().createQuery(hql);
		if (params != null && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		List<T> l = q.list();
		if (l != null && l.size() > 0) {
			return l.get(0);
		}
		return null;
	}
	
	/**
	 * 根据HQL查询实体，带参数
	 * @param <T>
	 * @param hql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T get(String hql, Object... values){  
		Query queryObject = this.getCurrentSession().createQuery(hql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				queryObject.setParameter(i, values[i]);
			}
		}
		return (T) queryObject.setMaxResults(1).uniqueResult();  
	} 
	
	/**
	 * 根据HQL查询返回Map
	 * @param hql
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected Map getForMap(String hql) {
		Query q = this.getCurrentSession().createQuery(hql);
		List<T> l = q.list();
		if (l != null && l.size() > 0) {
			return (Map) l.get(0);
		}
		return null;
		
	}

	/**
	 * 暂时通用
	 * @param hql
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private List<T> find(String hql) {
		Query q = this.getCurrentSession().createQuery(hql);
		return q.list();
	}
	
	
	@SuppressWarnings("unchecked")
	protected List<Map> findForMap(String hql) {
		Query q = this.getCurrentSession().createQuery(hql);
		return q.list();
	}
	
	/**
	 * 暂时停用
	 * @param hql
	 * @param params
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private List<T> find(String hql, Map<String, Object> params) {
		Query q = this.getCurrentSession().createQuery(hql);
		if (params != null && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return q.list();
	}

	/**
	 * 获取List
	 * @param hql
	 * @param values
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected List<T> find(String hql, Object ...values) {
		Query q = this.getCurrentSession().createQuery(hql);
		if(values != null){
			for (int i = 0; i < values.length; i++) {
				q.setParameter(i, values[i]);
			}
		}
		return q.list();
	}
	
	/**
	 * 暂时停用
	 * @param hql
	 * @param params
	 * @param page
	 * @param rows
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private List<T> find(String hql, Map<String, Object> params, int page,
			int rows) {
		Query q = this.getCurrentSession().createQuery(hql);
		if (params != null && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
	}

	/**
	 * 分页list
	 * @param hql
	 * @param page
	 * @param rows
	 * @param values
	 * @return
	 */
	protected List<T> find(String hql, int page, int rows,Object ...values) {
		Query q = this.getCurrentSession().createQuery(hql);
		
		if(values != null){
			for (int i = 0; i < values.length; i++) {
				q.setParameter(i, values[i]);
			}
		}
		
		return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
	}

	/**
	 * 分页查询List
	 * @param hql
	 * @param page
	 * @param rows
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected List<T> find(String hql, int page, int rows) {
		Query q = this.getCurrentSession().createQuery(hql);
		return q.setFirstResult((page - 1) * rows).setMaxResults(rows).list();
	}
	
	/**
	 * 统计条数
	 * @param hql
	 * @return
	 */
	protected Long count(String hql) {
		Query q = this.getCurrentSession().createQuery(hql);
		return (Long) q.uniqueResult();
	}
	
	/**
	 * 暂时通用
	 * @param hql
	 * @param params
	 * @return
	 */
	private Long count(String hql, Map<String, Object> params) {
		Query q = this.getCurrentSession().createQuery(hql);
		if (params != null && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return (Long) q.uniqueResult();
	}

	/**
	 * 统计条数
	 * @param hql
	 * @param values
	 * @return
	 */
	protected Long count(String hql, Object ...values) {
		Query q = this.getCurrentSession().createQuery(hql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				q.setParameter(i, values[i]);
			}
		}
		return (Long) q.uniqueResult();
	}
	
	/**
	 * 分页查询(单表)(暂时停用)
	 * @param page
	 * @param hql
	 * @param params
	 * @return
	 */
	private Page<T> queryPageForHql(Page<T> page, String hql, Map<String, Object> params) {
		
		List<T> queryList = this.find(hql, params ,page.getPageNo(), page.getPageSize());
		
		String countHql = " SELECT COUNT(id) "+hql;
		
		Long totalCount = this.count(countHql,params);
		
		if(queryList != null && queryList.size() >0){
			page.setTotalCount(totalCount);
		}
		
		page.setResult(queryList);
		
		return page;
	}
	
	/**
	 * 分页查询(单表)
	 * @param page
	 * @param hql
	 * @param params
	 * @return
	 */
	protected Page<T> queryPageForHql(Page<T> page, String hql,Object...values) {
		
		List<T> queryList = this.find(hql,page.getPageNo(), page.getPageSize(),values);
		
		String countHql = " SELECT COUNT(id) "+hql;
		
		Long totalCount = this.count(countHql,values);
		
		if(queryList != null && queryList.size() >0){
			page.setTotalCount(totalCount);
		}
		
		page.setResult(queryList);
		
		return page;
	}
	
	/**
	 * 分页查询(单表new)
	 * @param page
	 * @param hql
	 * @param params
	 * @return
	 */
	protected Page<T> queryPageForHql(T t, String hql,Object...values) {
		Page<T> page = new Page<T>((Integer)PropertyUtils.getProperty(t, "pageSize"));
		Integer pageNo = PropertyUtils.getProperty(t, "pageNo");
		page.setPageNo(pageNo);
		List<T> queryList = this.find(hql,page.getPageNo(), page.getPageSize(),values);
		
		String countHql = " SELECT COUNT(id) "+hql;
		
		Long totalCount = this.count(countHql,values);
		
		if(queryList != null && queryList.size() >0){
			page.setTotalCount(totalCount);
		}
		page.setResult(queryList);
		
		return page;
	}

	/**
	 * 根据sql查询集合(多表)
	 * @param sql
	 * @return
	 */
	protected List<Map<String,Object>> findListBySql(String sql){
		Query query = this.getCurrentSession().createSQLQuery(sql);
		query.setResultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP);
		return query.list();
	}

	/**
	 * 根据sql查询单条数据(多表)
	 * @param sql
	 * @return
	 */
	protected Map<String,Object> findMapBySql(String sql){
		Query query = this.getCurrentSession().createSQLQuery(sql);
		query.setResultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP);
		List<Map<String,Object>> list = query.list();
		if(list == null || list.size() == 0){
			return null;
		}else if(list != null && list.size() > 1){
			throw new RuntimeException("查询错误,返回多个结果. " + sql);
		}else{
			return (Map)list.get(0);
		}
	}

	/**
	 * 根据sql分页查询（多表）
	 * @param page
	 * @param sql
	 * @return
	 */
	protected Page queryPageForSql(Page page, String sql){
		
		int countNum = getCountRowsBySql(sql);
		
		Query query = this.getCurrentSession().createSQLQuery(sql);
		
		query.setFirstResult((page.getPageNo()-1)*page.getPageSize());
		query.setMaxResults(page.getPageSize());
		query.setResultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP);
		
		page.setTotalCount(countNum);
		page.setResult(query.list());
		
		return page;
	}
	
	/**
	 * 根据sql分页查询（多表new）
	 * @param page
	 * @param sql
	 * @return
	 */
	protected Page queryPageForSql(String sql,T t){
		Page page = new Page((Integer)PropertyUtils.getProperty(t, "pageSize"));
		int countNum = getCountRowsBySql(sql);
		Query query = this.getCurrentSession().createSQLQuery(sql);
		Integer maxResults = PropertyUtils.getProperty(t, "pageSize");
		Integer pageNo = PropertyUtils.getProperty(t, "pageNo");
		query.setFirstResult((pageNo-1)*maxResults);
		query.setMaxResults(maxResults);
		query.setResultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP);
		
		page.setTotalCount(countNum);
		page.setResult(query.list());
		page.setPageNo(pageNo);
		return page;
	}
	
	/**
	 * 根据sql分页查询（多表new）
	 * @param page
	 * @param sql
	 * @return
	 */
	protected Page queryPageForSql(String sql,T t,Object... params){
		Page page = new Page((Integer)PropertyUtils.getProperty(t, "pageSize"));
		int countNum = getCountRowsBySql(sql,params);
		Query query = this.getCurrentSession().createSQLQuery(sql);
		Integer maxResults = PropertyUtils.getProperty(t, "pageSize");
		Integer pageNo = PropertyUtils.getProperty(t, "pageNo");
		query.setFirstResult((pageNo-1)*maxResults);
		query.setMaxResults(maxResults);
		query.setResultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP);
		for (int i = 0; i < params.length; i++) {
			query.setParameter(i, params[i]);
		}
		page.setTotalCount(countNum);
		page.setResult(query.list());
		page.setPageNo(pageNo);
		return page;
	}
	
	/**
	 * 统计数据条数
	 * @param querySql
	 * @return
	 */
	private int getCountRowsBySql(final String querySql) {
		StringBuffer sql = new StringBuffer();
		sql.append("select count(1) b from (").append(querySql).append(") a");
		BigInteger count = (BigInteger) this.getCurrentSession().createSQLQuery(sql.toString()).uniqueResult();
		return count.intValue();
	}
	
	/**
	 * 统计数据条数
	 * @param querySql
	 * @return
	 */
	private int getCountRowsBySql(final String querySql,Object... params) {
		StringBuffer sql = new StringBuffer();
		sql.append("select count(1) b from (").append(querySql).append(") a");
		Query query = this.getCurrentSession().createSQLQuery(sql.toString());
		for (int i = 0; i < params.length; i++) {
			query.setParameter(i, params[i]);
		}
		BigInteger count = (BigInteger)query.uniqueResult();
		return count.intValue();
	}
	
	/**
	 * 根据T的属性值为条件查询Page
	 * @param t
	 * @return
	 */
	public Page<T> queryPage(T t) {
		// TODO Auto-generated method stub
		Page<T> page = new Page<T>((Integer)PropertyUtils.getProperty(t, "pageSize"));
		setDr(t);
		Criteria criteria = prepareQueryCriteria(t);
		criteria.setProjection(Projections.rowCount());
		Object result = criteria.uniqueResult();
		page.setTotalCount((Long)result);
		page.setPageNo((Integer)PropertyUtils.getProperty(t, "pageNo"));
		Integer maxResults = PropertyUtils.getProperty(t, "pageSize");
		if (null != maxResults) {
			criteria.setMaxResults(maxResults);
		}
		Integer pageNo = PropertyUtils.getProperty(t, "pageNo");
    	if (null != pageNo) {
    		criteria.setFirstResult((pageNo-1)*maxResults);
    	}
		Order order = PropertyUtils.getProperty(t, "cOrder");
		if(order!=null){			
			criteria.addOrder(order);
		}
		
		List<Order> rank = PropertyUtils.getProperty(t, "rank");
		
		if(ListUtil.isNotEmpty(rank)){
			for(Order rk:rank){
				criteria.addOrder(rk);
			}
		}
		
		criteria.setProjection(null);
		List list = criteria.list();
		page.setResult(list);
		return page;
	}
	
	/**
	 * 根据T的属性值为条件查询集合
	 * @param t
	 * @return
	 */
	public List<T> query(T t) {
	    setDr(t);
		Criteria criteria = prepareQueryCriteria(t);
		Order order = PropertyUtils.getProperty(t, "cOrder");
		if(order!=null){			
			criteria.addOrder(order);
		}
		
		List<Order> rank = PropertyUtils.getProperty(t, "rank");
		
		if(ListUtil.isNotEmpty(rank)){
			for(Order rk:rank){
				criteria.addOrder(rk);
			}
		}
		
		return criteria.list();
	}

    private void setDr(T t) {
        if(t.getDr()==null){	        
	        t.setDr(0);
	    }
    }
	
	public T queryEntity(T t) {
	    setDr(t);
		Criteria criteria = prepareQueryCriteria(t);
		Order order = PropertyUtils.getProperty(t, "cOrder");
		if(order!=null){			
			criteria.addOrder(order);
		}
		
		List<Order> rank = PropertyUtils.getProperty(t, "rank");
		
		if(ListUtil.isNotEmpty(rank)){
			for(Order rk:rank){
				criteria.addOrder(rk);
			}
		}
		
		return (T)criteria.uniqueResult();
	}

	private Criteria prepareQueryCriteria(T t) {
		Criteria criteria = getCurrentSession().createCriteria(t.getClass());
    	criteria = ConditionQuery.addCondition(criteria, t.getClass(), t);
    	return criteria;
	}	
	
	private void setDrNull(T t){
	    Field[] fields = PropertyUtils.getAllDeclaredFields(t.getClass());
	    for (Field f : fields) {
	        try {
	            Object value = PropertyUtils.getProperty(t, f.getName());
	            if(value instanceof BaseEntity){
	                PropertyUtils.setProperty(value, "dr",null);
//	                setDrNull((T)value);
	            }
            } catch (Exception e) {
                // TODO: handle exception
                continue;
            }

        }
	}
}

